# Drupal 11 Content Management System

## Introduction

Drupal 11 is a modern, enterprise-grade content management framework built on PHP 8.3+ and Symfony 7 components. Unlike Drupal 7's procedural architecture, Drupal 11 utilizes a fully Object-Oriented Programming (OOP) architecture, leveraging a Dependency Injection Container, Composer for package management, and a plugin-based system for extensibility.

At its core, Drupal 11 treats content as **Entities** (typed objects). It separates configuration (YAML-based) from content (Database-based). The system relies on PSR-4 autoloading, namespaced classes, and event subscribers. While legacy hooks exist, Drupal 11 introduces **Hook Attributes** and emphasizes Event Subscribers. Output is handled via the Twig templating engine, and routing is defined via YAML files mapping paths to Controller classes.

## APIs and Key Functions

### Entity API - Content CRUD Operations

The Entity API is the primary method for interacting with data. Nodes, Users, Terms, and custom entities are all instances of classes implementing `EntityInterface`. Direct database access for content is discouraged.

```php
use Drupal\node\Entity\Node;
use Drupal\Core\File\FileSystemInterface;

// Create a new article node
$node = Node::create([
  'type' => 'article',
  'title' => 'Getting Started with Drupal 11',
  'langcode' => 'en',
  'uid' => 1, // Author ID
  'status' => Node::PUBLISHED,
  'body' => [
    'value' => 'Drupal 11 is a powerful framework...',
    'format' => 'full_html',
  ],
  // Entity reference (Taxonomy) uses target_id
  'field_tags' => [
    ['target_id' => 5],
    ['target_id' => 8],
  ],
]);

// Save the node
$node->save();
echo "Created node with ID: " . $node->id();

// Load an existing node
$node = Node::load(1);
if ($node instanceof Node) {
  // Access fields using Magic Getters or get()
  echo $node->getTitle();
  echo $node->body->value;
  echo $node->get('body')->value; // more explicit

  // Access Entity Reference fields
  if (!$node->get('field_image')->isEmpty()) {
    $entity = $node->field_image->entity;
    $url = $entity->createFileUrl();
  }
}

// Load multiple nodes
$nids = [1, 2, 3, 4, 5];
$nodes = Node::loadMultiple($nids);
foreach ($nodes as $node) {
  echo $node->label() . "\n";
}

// Update a node
$node = Node::load(1);
$node->setTitle('Updated Article Title');
$node->set('body', ['value' => 'Updated content...', 'format' => 'basic_html']);
$node->field_category[] = ['target_id' => 10]; // Append value
$node->setNewRevision(TRUE);
$node->revision_log = 'Updated article info';
$node->save();

// Delete a node
$node = Node::load(1);
$node->delete();

// Delete multiple nodes (loading them first is best practice to trigger hooks)
$nodes = Node::loadMultiple([5, 6, 7]);
$storage_handler = \Drupal::entityTypeManager()->getStorage('node');
$storage_handler->delete($nodes);

// Get node by properties (using Entity Storage)
$nodes = \Drupal::entityTypeManager()
  ->getStorage('node')
  ->loadByProperties(['title' => 'My Article', 'status' => 1]);
$node = reset($nodes);
```

### Entity Query API - Complex Content Queries

Replaces `EntityFieldQuery`. Used to find entity IDs based on field values or properties. Always uses fluent interfaces.

```php
// Basic node query
$query = \Drupal::entityQuery('node')
  ->condition('type', 'article')
  ->condition('status', 1)
  ->condition('field_category.entity.name', 'Tech') // Query referenced entity data
  ->sort('created', 'DESC')
  ->range(0, 10)
  ->accessCheck(TRUE); // MANDATORY in D10/11

$nids = $query->execute();
$nodes = \Drupal\node\Entity\Node::loadMultiple($nids);

// Complex conditions (AND/OR groups)
$query = \Drupal::entityQuery('node')
  ->condition('status', 1)
  ->accessCheck(TRUE);

$group = $query->orConditionGroup()
  ->condition('field_price', 100, '>')
  ->condition('field_stock', 0, '>');

$query->condition($group);
$result = $query->execute();

// Date range query
$query = \Drupal::entityQuery('node')
  ->condition('field_date.value', ['2024-01-01', '2024-12-31'], 'BETWEEN')
  ->accessCheck(TRUE)
  ->execute();

// Count results only
$count = \Drupal::entityQuery('node')
  ->condition('type', 'article')
  ->accessCheck(TRUE)
  ->count()
  ->execute();
```

### Database API

Used for non-entity data or performance-critical reporting. Access is typically handled via Dependency Injection (`@database` service), but static access exists for legacy support or scripts.

```php
use Drupal\Core\Database\Database;

$connection = \Drupal::database();

//